<template>
  <el-table
    ref="Tableref"
    :data="state.tableData"
    :stripe="stripe"
    :border="border"
    v-loading="$attrs.vLoading"
    highlight-current-row
    v-bind="$attrs"
    :cell-class-name="cellClassGroup"
    :span-method="spanMethod"
    @select="selectRow"
    @select-all="selectAll"
    @selection-change="handleSelectionChange"
    @row-click="rowClick"
    :row-class-name="rowClassName"
  >
    <el-table-column
      v-for="(item, index) in vaildColumn"
      :show-overflow-tooltip="item.showOverflowTooltip || !item.type"
      v-bind="{ ...item }"
      :key="item.prop"
    >
      <template v-if="item.custom" #default="scope">
        <slot :name="item.prop" v-bind="scope"> </slot>
      </template>

      <template #default="Scope" v-else-if="item.render">
        <component :is="item.render(h, Scope)"> </component>
      </template>

      <template v-else-if="item.mspan && item.mspan === 'line'" #default="scope">
        <div v-if="isGather(scope.row[item.prop], item.prop)" class="gather">
          <div class="square_brackets"></div>
        </div>
        <span v-else></span>
      </template>

      <template #default="{ row, column, $index }" v-else>
        <template v-if="row?.edit && item.subassembly">
          <el-form
            :inline="true"
            :show-message="false"
            :model="row"
            :ref="`tb-${item.prop}-${$index}`"
            v-if="isRequired(item, row)"
            @submit.prevent
          >
            <el-form-item :prop="item.prop ?? null" :rules="formattRules(item, row)">
              <AutoComponents
                v-model="row[item.prop]"
                v-model:inputlabel="row[item.subOptions?.inputlabel]"
                :is="item.subassembly"
                v-on="getEventHandlers(item.subOptions?.event, row, column, $index)"
                v-focus="row[item.prop + 'focus'] || null"
                :subOptions="formattSubOptions(item, row)"
              />
            </el-form-item>
          </el-form>
          <!-- <v-form :inline="true" :show-message="false" :model="row" :ref="`tb${item.prop}${$index}`" v-if="item.required"> </v-form> -->
          <template v-else>
            <AutoComponents
              v-model="row[item.prop]"
              v-model:inputlabel="row[item.subOptions?.inputlabel]"
              :is="item.subassembly"
              v-on="getEventHandlers(item.subOptions?.event, row, column, $index)"
              v-focus="row[item.prop + 'focus'] || null"
              :subOptions="formattSubOptions(item, row)"
            />
          </template>
        </template>
        <template v-if="!row.edit && item.subOptions && item.subOptions.inputlabel && !item.formatter">
          {{ row[item.subOptions?.inputlabel] }}</template
        >
      </template>
    </el-table-column>
    <el-table-column
      label="操作"
      fixed="right"
      :width="rwidth || 12 + operate.length * 40"
      v-if="operate && Object.keys(operate).length"
    >
      <template #default="scope">
        <el-button link type="primary" @click="modify(scope)" v-if="operate.includes('E')"> 编辑 </el-button>
        <el-button link type="primary" @click="save(scope)" v-if="operate.includes('S')"> 保存 </el-button>
        <el-button link type="danger" @click="remove(scope)" v-if="operate.includes('D')">删除</el-button>
        <slot name="operate" v-bind="scope"></slot>
      </template>
    </el-table-column>
  </el-table>
</template>

<script lang="ts" setup>
  import { getCurrentInstance, ref, watch, nextTick, watchEffect } from 'vue'
  import type { FormValidateCallback } from 'element-plus'
  import AutoComponents from './autoComponents'
  import { vFocus } from '@v-model/shared--directives'
  import { rowspan } from './rowspan'
  // import { Tableref } from './reactiveMtable'
  import { hasSelected, setFoucskey } from './rowspan'
  import { vtableProps } from './vtableProps'
  import { useTable } from './use-table'
  import { Ref, h } from 'vue'
  import { VTableRef } from './table'
  import { useScroll } from './use-scroll'

  const { proxy } = getCurrentInstance()!

  defineOptions({ name: 'VTable' })

  // 缓存请求数据
  const cachSource: Ref<{ [k: string]: string }> = ref({})

  const Tableref = ref<VTableRef>()!
  const emits = defineEmits(['modify', 'save', 'remove', 'select', 'selection-change', 'row-click'])

  const props = defineProps(vtableProps)

  const {
    vaildColumn,
    formatData,
    isRequired,
    formattRules,
    formattSubOptions,
    cellClassGroup,
    modify,
    save,
    remove,
    state,
    getEventHandlers,
  } = useTable(props, Tableref)

  // 合并单元格
  const {
    selectRow,
    spanMethod,
    isGather,
    selectAll,
    handleSelectionChange,
    toggleRowSelection,
    getSelectionRows,
    setCurrentRow,
    toggleAllSelection,
    setScrollTop,
  } = rowspan(props, Tableref)

  // 开启滚动 虚拟列表优化
  const { updateVisibleContent } = useScroll(props, Tableref, state)

  const getList = (url: string, params: any) => {
    return new Promise((resolve, reject) => {
      proxy!.$http.requestJSON('post', url, params).then((res: any) => {
        if (res.data) {
          resolve(Array.isArray(res.data) ? res.data : res.data.records)
        }
      })
    })
  }

  // 分组线——————end
  watch(
    () => props.columns,
    async (n, o) => {
      // 将相同字典数据缓存，下次直接调用
      props.columns.forEach((col: any) => {
        const url = col.subOptions?.url
        if (col.subOptions?.cached && url) {
          if (cachSource.value[url]) {
            col.subOptions.options = cachSource.value[url]
          } else {
            // 将 url 地址缓存， 将原 url 清空以防止再次请求
            col.subOptions.cachedUrl = col.subOptions.url
            col.subOptions.url = ''

            getList(url, col.subOptions.params).then((res) => {
              cachSource.value[url] = res
              col.subOptions.options = res
            })
          }
        }
      })
    },
    { immediate: true }
  )

  watchEffect(() => {
    // 是否开启虚拟列表，视图只展示可视区域
    if (props.virtualize) {
      if (!Tableref.value) {
        nextTick(() => {
          updateVisibleContent()
        })
      } else {
        updateVisibleContent()
      }
    } else {
      state.tableData = props.data
    }

    // 根据是否分组来判断是否需要添加分组样式
    if (props.isGroup) {
      formatData(state.tableData, props.pageNum, props.pageSize)
    }
  })

  // 数据保存验证
  const validateList = async (forlist: any, formKey: string = '') => {
    let flag = true
    const formList: FormValidateCallback[] = []
    Object.keys(forlist).map((key) => {
      if (key.includes(formKey) && forlist[key] && forlist[key].length && forlist[key][0].hasOwnProperty('validate')) {
        const validRef = Array.isArray(forlist[key]) ? forlist[key][0] : forlist[key]
        Array.isArray(forlist[key]) ? formList.push(...forlist[key]) : formList.push(forlist[key])

        validRef.validate?.((valid, fields) => {
          if (!valid) {
            // 是否在没有填写验证数据的时候自动聚焦到当前没填写的字段
            if (props.unvalidjump) {
              if (flag) {
                // 第一条未验证通过的设置聚焦操作
                const refArr = key.split('-')
                setFoucskey(state.tableData[refArr[2]], refArr[1])
              }
              Object.keys(fields).forEach((key) => {
                proxy!.$message({ type: 'warning', message: fields[key][0].message })
              })
            }
            flag = false
          }
        })
      }
      return key
    })
    for (let i in formList) {
      await formList[i].validate?.((valid, fields) => {
        if (!valid) flag = false
      })
    }
    return Promise.resolve(flag)
  }

  const validate = async () => {
    const valid = await validateList(proxy!.$refs)
    return Promise.resolve(valid)
  }

  const rowClick = (row: any, column: any, event: any) => {
    // 点击行时，是否勾选当前行状态切换 , 只有点击 div 和 td 时才选中
    if (props.clickToggleSelected && ['DIV', 'TD'].includes(event.target.tagName)) {
      const selections = getSelectionRows()
      toggleRowSelection(row, !hasSelected(selections, row))
    }
    emits('row-click', row, column, event)
  }

  defineExpose({ validate, Tableref, toggleRowSelection, getSelectionRows, setCurrentRow, toggleAllSelection, setScrollTop })
</script>

<style lang="scss">
  .gather {
    position: absolute;
    top: 10px;
    bottom: 10px;
    width: 20px;
    right: 4px;
    overflow: hidden;
    .square_brackets {
      display: flex;
      align-items: center;
      justify-content: center;
      border: none;
      position: relative;
      padding: 0;
      height: 100%;
      &:before {
        content: '';
        display: block;
        height: 100%;
        width: 15px;
        border: 2px solid var(--el-color-danger);
        position: absolute;
        top: 0;
        left: 10px;
        border-right: 0;
      }
    }
  }

  .v-line-center::before,
  .v-line-start::before,
  .v-line-stop::before {
    position: absolute;
    content: '';
    left: 3px;
    width: 8px;
    margin-right: 10px;
    border-left: 2px solid red;
  }
  .v-line-start::before {
    bottom: -1px;
    top: 12px;
    border-top: 2px solid red;
  }
  .v-line-center::before {
    bottom: -7px;
    top: -7px;
  }
  .v-line-stop::before {
    bottom: 14px;
    top: -7px;
    border-bottom: 2px solid red;
  }
</style>

<style lang="scss" scoped>
  .el-table {
    :deep(.v-group .cell.el-tooltip) {
      padding-left: 13px;
    }

    // .el-input {
    :deep(.el-input) {
      width: 100%;
    }
  }
</style>
